package com.googlecode.jlsf.swing;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.net.UnknownHostException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.DefaultFormatter;

import com.googlecode.jlsf.IP;

/**
 * Hosts ranges manipulating panel 
 */
public class LeftPanel extends JPanel 
{
	private static final long serialVersionUID = 1267276891390083121L;

	private static JList ranges;
	private DefaultListModel rangesListModel;
	private JFormattedTextField title, fromIp, toIp, netmask;
	private JButton buttonAdd, buttonEdit, buttonDelete;
	private static ArrayList<Range> rangesList;
	private static Range selectedRange;
	private boolean rangeWasChanged = false;
	
	protected LeftPanel()
	{
		super(new BorderLayout(4, 4));
		this.setBorder(new EmptyBorder(4, 4, 4, 4));
		
		rangesListModel = new DefaultListModel();
		ranges = new JList(rangesListModel);
		JScrollPane rangesPanel = new JScrollPane(ranges);
		this.add(rangesPanel, BorderLayout.CENTER);
		
		ranges.setLayoutOrientation(JList.VERTICAL);
		ranges.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		
		rangesList = new ArrayList<Range>();
		
		ranges.addListSelectionListener
		(
			new ListSelectionListener()
			{
				public void valueChanged(ListSelectionEvent evt)
				{
					String selectedTitle = (String)ranges.getSelectedValue();
					
					for(Range r : rangesList)
					{
						if(selectedTitle == r.toString())
						{
							selectedRange = r;
							break;
						}
					}
					
					if(selectedRange != null)
					{
						setFields(selectedRange, false);
						
						buttonAdd.setEnabled(true);
						buttonEdit.setEnabled(true);
						buttonDelete.setEnabled(true);
						
						RightPanel.setSelectedRangeNode(selectedRange);
					}
					
				}
			}
		);
		
		JPanel ipPanel = new JPanel(new BorderLayout(4, 4));
		this.add(ipPanel, BorderLayout.NORTH);
		
		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 4, 4));
		ipPanel.add(buttonsPanel, BorderLayout.SOUTH);
		
		buttonAdd = new JButton(getAddAction());
		buttonAdd.setEnabled(true);
		buttonsPanel.add(buttonAdd);
		
		buttonEdit = new JButton(getEditAction());
		buttonEdit.setEnabled(false);
		buttonsPanel.add(buttonEdit);
		
		buttonDelete = new JButton(getDeleteAction());
		buttonDelete.setEnabled(false);
		buttonsPanel.add(buttonDelete);
	
		JPanel westGrid = new JPanel(new GridLayout(4, 1, 4, 4));
		ipPanel.add(westGrid, BorderLayout.WEST);
		
		westGrid.add(new JLabel(ResourceManager.getString("range.title")));
		westGrid.add(new JLabel(ResourceManager.getString("range.ip.start")));
		westGrid.add(new JLabel(ResourceManager.getString("range.ip.finish")));
		westGrid.add(new JLabel(ResourceManager.getString("range.netmask")));
		
		JPanel centerGrid = new JPanel(new GridLayout(4, 1, 4, 4));
		ipPanel.add(centerGrid, BorderLayout.CENTER);
		
		title = new JFormattedTextField();
		centerGrid.add(title);
		
		fromIp = new JFormattedTextField(new IPAddressFormatter());
		centerGrid.add(fromIp);
		
		toIp = new JFormattedTextField(new IPAddressFormatter());
		centerGrid.add(toIp);
		
		netmask = new JFormattedTextField(NumberFormat.getIntegerInstance());
		centerGrid.add(netmask);
		
		this.setDefaultFields();
		
	}
	
	private Action getAddAction()
	{
		AbstractAction a = new AbstractAction(ResourceManager.getString("action.add"),
				ResourceManager.getIcon("add.png")) 
		{
			private static final long serialVersionUID = -4379887901334588304L;

			public void actionPerformed(ActionEvent e)
			{
				if(( ! rangeWasChanged) && (selectedRange != null))
				{
					selectedRange = null;
					
					setDefaultFields();
					
					buttonAdd.setEnabled(true);
					buttonEdit.setEnabled(false);
					buttonDelete.setEnabled(false);		
					
					return;
				}
				
				String titleValue;
				IP fromIpValue = null, toIpValue = null;
				int netmaskValue;
				
				if((title.getValue() != null) && (netmask.getValue() != null) &&
						(fromIp.getValue() != null) && (toIp.getValue() != null))
				{
					titleValue = (String)title.getValue();
					netmaskValue = ((Number)netmask.getValue()).intValue();
				
					try 
					{
						fromIpValue = new IP((byte[])fromIp.getValue(), netmaskValue);
						toIpValue = new IP((byte[])toIp.getValue(), netmaskValue);
					} 
					catch (UnknownHostException e1) 
					{
						e1.printStackTrace();
					} 
					catch (IllegalArgumentException e1) 
					{
						e1.printStackTrace();
					}
				}	
				else 
					return;
				
				Range r = new Range(titleValue, fromIpValue, toIpValue, netmaskValue);

				if(( ! rangeWasChanged) && ( ! rangesListModel.contains(r.toString())))
				{
					rangesListModel.addElement(r.toString());
					rangesList.add(r);
					RightPanel.activateButtonScan(true);
					
					rangeWasChanged = false;
					
					return;
				}
				
				if(rangeWasChanged && (selectedRange != null))
				{
					rangesList.remove(selectedRange);
					rangesListModel.removeElement(selectedRange.toString());
					
					rangesListModel.addElement(r.toString());
					rangesList.add(r);
					
					setDefaultFields();
					
					buttonAdd.setEnabled(true);
					buttonEdit.setEnabled(false);
					buttonDelete.setEnabled(false);
					
					rangeWasChanged = false;
					
					selectedRange = null;
				}
				
			}
			
		};
		
		a.putValue(Action.SHORT_DESCRIPTION, ResourceManager.getString("action.add.description"));
		
		return a;
	}
	
	private Action getEditAction()
	{
		AbstractAction a = new AbstractAction(ResourceManager.getString("action.edit"),
				ResourceManager.getIcon("edit.png")) 
		{
			private static final long serialVersionUID = -6408637871806908166L;

			public void actionPerformed(ActionEvent e)
			{
				if(selectedRange != null)
				{
					buttonAdd.setEnabled(true);
					buttonEdit.setEnabled(false);
				
					setFields(selectedRange, true);

					rangeWasChanged = true;
				}
				
			}
			
		};
		
		a.putValue(Action.SHORT_DESCRIPTION, ResourceManager.getString("action.edit.description"));
		
		return a;
	}
	
	private Action getDeleteAction()
	{
		AbstractAction a = new AbstractAction(ResourceManager.getString("action.delete"),
				ResourceManager.getIcon("del.png")) 
		{
			private static final long serialVersionUID = 2254558841231792362L;

			public void actionPerformed(ActionEvent e)
			{
				if(selectedRange != null)
				{
					rangesListModel.removeElement(selectedRange.toString());
					rangesList.remove(selectedRange);
					
					selectedRange = null;
					
					rangeWasChanged = false;
					
					if(rangesList.isEmpty())
						RightPanel.activateButtonScan(false);
					
					setDefaultFields();
					
					buttonAdd.setEnabled(true);
					buttonEdit.setEnabled(false);
					buttonDelete.setEnabled(false);
				}
				
			}
			
		};
		
		a.putValue(Action.SHORT_DESCRIPTION, ResourceManager.getString("action.delete.description"));
		
		return a;
	}
	
	/**
	 * Sets default values into ip range's params fields
	 */
	private void setDefaultFields()
	{
		title.setValue("New range");
		title.setEditable(true);
		
		fromIp.setValue(new byte[] {(byte) 192, (byte) 168, (byte) 0, (byte) 1});
		fromIp.setEditable(true);
		
		toIp.setValue(new byte[] {(byte) 192, (byte) 168, (byte) 254, (byte) 254});
		toIp.setEditable(true);
		
		netmask.setValue(new Integer(16));
		netmask.setEditable(true);
	}
	
	/**
	 * Sets params of selected range into ip range's params fields
	 */
	private void setFields(Range r, boolean editable)
	{
		title.setValue(r.toString());
		title.setEditable(editable);
		
		fromIp.setValue(r.getIPs()[0].getByteFields());
		fromIp.setEditable(editable);
		
		toIp.setValue(r.getIPs()[1].getByteFields());
		toIp.setEditable(editable);
		
		netmask.setValue(r.getNetmask());
		netmask.setEditable(editable);
	}
	
	protected static void setSelectedRange(Range r)
	{
		ranges.setSelectedValue(r.toString(), true);
		selectedRange = r;
	}
	
	public static ArrayList<Range> getRangesList()
	{
		return rangesList;
	}
	
	/**
	* A formatter for IPv4 addresses (a.b.c.d)
	*/
	private class IPAddressFormatter extends DefaultFormatter
	{
		private static final long serialVersionUID = -7020511620706568766L;

		public String valueToString(Object value) throws ParseException
		{
		   if (!(value instanceof byte[])) 
		      throw new ParseException("Not a byte[]", 0);
		   
		   byte[] a = (byte[]) value;
		   
		   if (a.length != 4) 
		      throw new ParseException("Length != 4", 0);
		   
		   StringBuilder builder = new StringBuilder();
		   
		   for (int i = 0; i < 4; i++)
		   {
		      int b = a[i];
		      
		      if (b < 0) 
		    	  b += 256;
		      
		      builder.append(String.valueOf(b));
		      
		      if (i < 3) 
		    	  builder.append('.');
		   }
		   
		   return builder.toString();
		}
		
		public Object stringToValue(String text) throws ParseException
		{
		   StringTokenizer tokenizer = new StringTokenizer(text, ".");
		   
		   byte[] a = new byte[4];
		   
		   for (int i = 0; i < 4; i++)
		   {
		      int b = 0;
		      
		      if (!tokenizer.hasMoreTokens())
		         throw new ParseException("Too few bytes", 0);
		      
		      try
		      {
		         b = Integer.parseInt(tokenizer.nextToken());
		      }
		      catch (NumberFormatException e)
		      {
		         throw new ParseException("Not an integer", 0);
		      }
		      
		      if (b < 0 || b >= 256) 
		         throw new ParseException("Byte out of range", 0);
		      
		      a[i] = (byte) b;
		   }
		   
		   if (tokenizer.hasMoreTokens())
		      throw new ParseException("Too many bytes", 0);
		   
		   return a;
		}

	}
	
}